import logging

from django.contrib.auth import get_user_model
from django.db.models import Q
from django.http import JsonResponse
from django.shortcuts import render, redirect
from django.views.generic.base import View

from compute.managers import CenterManager, HostManager, GroupManager, ComputeError
from device.manager import PCIDeviceManager, DeviceError
from image.managers import ImageManager, ImageError
from image.models import Image
from logrecord.manager import user_operation_record
from network.managers import MacIPManager, VlanManager
from utils.errors import VmAccessDeniedError, VmNotExistError
from utils.ev_libvirt.virt import VmDomain
from utils.paginators import NumsPaginator
from vdisk.manager import VdiskManager, VdiskError
from vms.manager import VmSharedUserManager

from .api import VmAPI, VmBuilder
from .manager import VmManager, VmError, FlavorManager, VmLogManager
from .models import AttachmentsIP, Vm, VmArchive

logger = logging.getLogger('django.request')

User = get_user_model()


def str_to_int_or_default(val, default):
    """
    字符串转int，转换失败返回设置的默认值

    :param val: 待转化的字符串
    :param default: 转换失败返回的值
    :return:
        int     # success
        default # failed
    """
    try:
        return int(val)
    except Exception:
        return default


class VmsView(View):
    """
    虚拟机类视图
    """
    NUM_PER_PAGE = 20  # Show num per page

    def get(self, request, *args, **kwargs):
        center_id = str_to_int_or_default(request.GET.get('center', 0), 0)
        group_id = str_to_int_or_default(request.GET.get('group', 0), 0)
        host_id = str_to_int_or_default(request.GET.get('host', 0), 0)
        user_id = str_to_int_or_default(request.GET.get('user', 0), 0)
        search = request.GET.get('search', '')

        # 超级用户可以有用户下拉框选项
        auth = request.user
        if auth.is_superuser:
            users = User.objects.all()
        else:  # 普通用户只能查看自己的虚拟机，无用户下拉框选项
            users = [auth]
            user_id = auth.id

        v_manager = VmManager()
        try:
            queryset = v_manager.filter_vms_queryset(center_id=center_id, group_id=group_id, host_id=host_id,
                                                     search=search, user_id=user_id, all_no_filters=auth.is_superuser)

            queryset_shelve = v_manager.filter_shelve_vm_queryset(user_id=user_id, all_no_filters=auth.is_superuser)

        except VmError as e:
            error = VmError(msg='查询虚拟机时错误', err=e)
            return error.render(request=request)

        queryset = queryset.prefetch_related('vdisk_set')  # 反向预查询硬盘（避免多次访问数据库）
        try:
            c_manager = CenterManager()
            g_manager = GroupManager()
            centers = c_manager.get_center_queryset()
            if center_id > 0:
                # groups = c_manager.get_group_queryset_by_center(center_id)
                groups = c_manager.get_all_group_queryset_by_center(center_id)
            else:
                # groups = g_manager.get_group_queryset()
                groups = g_manager.get_all_group_queryset()

            if group_id > 0:
                hosts = g_manager.get_all_host_queryset_by_group(group_id)
            else:
                hosts = HostManager().get_all_hosts()
                # if request.user.is_superuser:
                #     hosts = HostManager().get_all_hosts()
                # else:
                #     hosts = None

            if len(groups) == 1:
                hosts = g_manager.get_all_host_queryset_by_group(groups[0].id)
            # if not hosts and len(groups) == 1:
            #     hosts = g_manager.get_all_host_queryset_by_group(groups[0].id)

        except ComputeError as e:
            error = ComputeError(msg='查询虚拟机时错误', err=e)
            return error.render(request=request)

        queryset = queryset.order_by('mac_ip__mac')  # 通过mac排序

        context = {'center_id': center_id if center_id > 0 else None, 'centers': centers, 'groups': groups,
                   'group_id': group_id if group_id > 0 else None, 'hosts': hosts,
                   'host_id': host_id if host_id > 0 else None, 'search': search, 'users': users, 'user_id': user_id,
                   'shelve_count': queryset_shelve.count()}
        context = self.get_vms_list_context(request, queryset, context)
        return render(request, 'vms_list.html', context=context)

    def get_vms_list_context(self, request, vms_queryset, context: dict):
        # 分页显示
        paginator = NumsPaginator(request, vms_queryset, self.NUM_PER_PAGE)
        page_num = request.GET.get(paginator.page_query_name, 1)  # 获取页码参数，没有参数默认为1
        vms_page = paginator.get_page(page_num)
        page_nav = paginator.get_page_nav(vms_page)

        context['page_nav'] = page_nav
        context['vms'] = vms_page
        context['count'] = paginator.count
        return context


class VmCreateView(View):
    """创建虚拟机类视图"""

    def get(self, request, *args, **kwargs):
        center_id = str_to_int_or_default(request.GET.get('center_id', 0), 0)

        groups = None
        images = None
        try:
            c_manager = CenterManager()
            centers = c_manager.get_center_queryset()
            if center_id == 0:
                if len(centers) > 0:
                    center_id = centers.first().id

            if center_id > 0:
                images = ImageManager().get_image_queryset_by_center(center_id).filter(tag=Image.TAG_BASE, enable=True)
                groups = c_manager.get_user_group_queryset_by_center(center_id, user=request.user)
        except (ComputeError, ImageError) as e:
            error = ComputeError(msg='查询数据中心列表时错误', err=e)
            return error.render(request=request)

        context = {
            'center_id': center_id if center_id > 0 else None,
            'centers': centers,
            'groups': groups,
            'image_tags': Image.CHOICES_TAG,
            'images': images,
            'flavors': FlavorManager().get_user_flaver_queryset(user=request.user)
        }
        return render(request, 'vms_create.html', context=context)


class VmMountDiskView(View):
    """虚拟机挂载硬盘类视图"""
    NUM_PER_PAGE = 20

    def get(self, request, *args, **kwargs):
        vm_uuid = kwargs.get('vm_uuid', '')
        search = request.GET.get('search', '')

        vm_manager = VmManager()
        vm = vm_manager.get_vm_by_uuid(vm_uuid=vm_uuid, related_fields=('host', 'host__group', 'image'))
        if not vm:
            try:
                raise VmNotExistError(msg='【挂载硬盘时错误】【虚拟机不存在】')
            except VmNotExistError as error:
                return error.render(request=request)

        group = vm.host.group
        user = request.user

        disk_manager = VdiskManager()
        related_fields = ('user', 'quota', 'quota__group')
        try:
            if user.is_superuser:
                queryset = disk_manager.filter_vdisk_queryset(group_id=group.id, search=search,
                                                              related_fields=related_fields)
            else:
                queryset = disk_manager.filter_vdisk_queryset(group_id=group.id, search=search,
                                                              user_id=user.id, related_fields=related_fields)
        except VdiskError as e:
            error = VdiskError(msg='查询硬盘列表时错误', err=e)
            return error.render(request=request)

        queryset = queryset.filter(vm=None).all()
        context = {'vm': vm, 'search': search}
        context = self.get_vdisks_list_context(request=request, queryset=queryset, context=context)
        return render(request, 'vm_mount_disk.html', context=context)

    def get_vdisks_list_context(self, request, queryset, context: dict):
        # 分页显示
        paginator = NumsPaginator(request, queryset, self.NUM_PER_PAGE)
        page_num = request.GET.get(paginator.page_query_name, 1)  # 获取页码参数，没有参数默认为1
        vms_page = paginator.get_page(page_num)
        page_nav = paginator.get_page_nav(vms_page)

        context['page_nav'] = page_nav
        context['vdisks'] = vms_page
        context['count'] = paginator.count
        return context


class VmDetailView(View):
    """虚拟机详情类视图"""

    def get(self, request, *args, **kwargs):
        vm_uuid = kwargs.get('vm_uuid', '')

        vm_manager = VmManager()
        vm = vm_manager.get_vm_by_uuid(vm_uuid=vm_uuid, related_fields=('host__group', 'image__ceph_pool',
                                                                        'mac_ip__vlan'))
        if not vm:
            try:
                raise VmNotExistError(msg='【挂载硬盘时错误】【虚拟机不存在】')
            except VmNotExistError as error:
                return error.render(request=request)

        vm_shared_users = VmSharedUserManager.get_vm_shared_users_qs(vm_id=vm_uuid)
        return render(request, 'vm_detail.html', context={'vm': vm, 'vm_shared_users': vm_shared_users})


class VmEditView(View):
    """虚拟机修改类视图"""

    def get(self, request, *args, **kwargs):
        vm_uuid = kwargs.get('vm_uuid', '')

        vm_manager = VmManager()
        vm = vm_manager.get_vm_by_uuid(vm_uuid=vm_uuid, related_fields=(
            'host', 'host__group', 'host__group__center', 'image', 'mac_ip'))
        if not vm:
            try:
                raise VmNotExistError(msg='虚拟机不存在')
            except VmNotExistError as error:
                return error.render(request=request)

        context = {
            'flavors': FlavorManager().get_user_flaver_queryset(user=request.user),
            'vm': vm
        }
        return render(request, 'vm_edit.html', context=context)


class VmResetView(View):
    """虚拟机重置系统镜像类视图"""

    def get(self, request, *args, **kwargs):
        vm_uuid = kwargs.get('vm_uuid', '')

        vm_manager = VmManager()
        vm = vm_manager.get_vm_by_uuid(vm_uuid=vm_uuid, related_fields=(
            'host', 'host__group', 'host__group__center', 'image', 'mac_ip'))
        if not vm:
            try:
                raise VmNotExistError(msg='虚拟机不存在')
            except VmNotExistError as error:
                return error.render(request=request)

        images = ImageManager().filter_image_queryset(
            center_id=vm.host.group.center_id, sys_type=0, tag=Image.TAG_BASE, search='')
        return render(request, 'vm_reset.html', context={'vm': vm, 'images': images})


class VmMigrateView(View):
    """虚拟机迁移类视图"""

    def get(self, request, *args, **kwargs):
        vm_uuid = kwargs.get('vm_uuid', '')

        vm_manager = VmManager()
        vm = vm_manager.get_vm_by_uuid(vm_uuid=vm_uuid, related_fields=(
            'host', 'host__group', 'host__group__center', 'image', 'mac_ip'))
        if not vm:
            try:
                raise VmNotExistError(msg='虚拟机不存在')
            except VmNotExistError as error:
                return error.render(request=request)

        hosts = HostManager().get_hosts_by_group_id(group_id=vm.host.group_id)
        hosts = list(filter(lambda host: host.id != vm.host_id, hosts))
        return render(request, 'vm_migrate.html', context={'vm': vm, 'hosts': hosts})


class VmLiveMigrateView(View):
    """虚拟机动态迁移类视图"""

    def get(self, request, *args, **kwargs):
        vm_uuid = kwargs.get('vm_uuid', '')

        vm_manager = VmManager()
        vm = vm_manager.get_vm_by_uuid(vm_uuid=vm_uuid, related_fields=(
            'host', 'host__group', 'host__group__center', 'image', 'mac_ip'))
        if not vm:
            try:
                raise VmNotExistError(msg='虚拟机不存在')
            except VmNotExistError as error:
                return error.render(request=request)

        hosts = HostManager().get_hosts_by_group_id(group_id=vm.host.group_id)
        hosts = list(filter(lambda host: host.id != vm.host_id, hosts))
        return render(request, 'vm_live_migrate.html', context={'vm': vm, 'hosts': hosts})


class VmMountPCIView(View):
    """虚拟机挂载PCI设备类视图"""
    NUM_PER_PAGE = 20

    def get(self, request, *args, **kwargs):
        vm_uuid = kwargs.get('vm_uuid', '')
        search = request.GET.get('search', '')

        vm_manager = VmManager()
        vm = vm_manager.get_vm_by_uuid(vm_uuid=vm_uuid, related_fields=('host__group', 'image'))
        if not vm:
            try:
                raise VmNotExistError(msg='【挂载PCI设备时错误】【虚拟机不存在】')
            except VmNotExistError as error:
                return error.render(request=request)

        host = vm.host
        user = request.user

        mgr = PCIDeviceManager()
        try:
            queryset = mgr.filter_pci_queryset(host_id=host.id, search=search,
                                               user=user, related_fields=('host__group',))
        except DeviceError as e:
            error = DeviceError(msg='查询PCI设备列表时错误', err=e)
            return error.render(request=request)

        context = {'vm': vm, 'search': search}
        context = self.get_pci_list_context(request=request, queryset=queryset, context=context)
        return render(request, 'vm_mount_pci.html', context=context)

    def get_pci_list_context(self, request, queryset, context: dict):
        # 分页显示
        paginator = NumsPaginator(request, queryset, self.NUM_PER_PAGE)
        page_num = request.GET.get(paginator.page_query_name, 1)  # 获取页码参数，没有参数默认为1
        page = paginator.get_page(page_num)
        page_nav = paginator.get_page_nav(page)

        context['page_nav'] = page_nav
        context['devices'] = page
        context['count'] = paginator.count
        return context


class VmSysDiskExpandView(View):
    """虚拟机系统盘扩容类视图"""

    def get(self, request, *args, **kwargs):
        vm_uuid = kwargs.get('vm_uuid', '')

        vm_manager = VmManager()
        vm = vm_manager.get_vm_by_uuid(vm_uuid=vm_uuid, related_fields=(
            'host', 'image__ceph_pool__ceph'))
        if not vm:
            try:
                raise VmNotExistError(msg='虚拟机不存在')
            except VmNotExistError as error:
                return error.render(request=request)

        if not vm.user_has_perms(request.user):
            try:
                raise VmAccessDeniedError(msg='没有此虚拟机的访问权限')
            except VmAccessDeniedError as error:
                return error.render(request=request)

        return render(request, 'vm_disk_expand.html', context={'vm': vm})


class VmShelveView(View):
    NUM_PER_PAGE = 20  # Show num per page

    def get(self, request, *args, **kwargs):
        user_id = str_to_int_or_default(request.GET.get('user', 0), 0)
        search = request.GET.get('search', '')

        v_manager = VmManager()

        # 超级用户可以有用户下拉框选项
        auth = request.user
        if auth.is_superuser:
            users = User.objects.all()
        else:  # 普通用户只能查看自己的虚拟机，无用户下拉框选项
            users = None
            user_id = auth.id

        try:
            queryset = v_manager.filter_shelve_vm_queryset(user_id=user_id, search=search,
                                                           all_no_filters=auth.is_superuser)
            queryset_normal = v_manager.filter_vms_queryset(user_id=user_id, all_no_filters=auth.is_superuser)
        except VmError as e:
            error = VmError(msg='查询虚拟机时错误', err=e)
            return error.render(request=request)

        queryset = queryset.prefetch_related('vdisk_set')  # 反向预查询硬盘（避免多次访问数据库）
        # 分页显示
        paginator = NumsPaginator(request, queryset, self.NUM_PER_PAGE)
        page_num = request.GET.get(paginator.page_query_name, 1)  # 获取页码参数，没有参数默认为1
        vms_page = paginator.get_page(page_num)
        page_nav = paginator.get_page_nav(vms_page)

        context = {'page_nav': page_nav, 'vms': vms_page, 'count': paginator.count, 'search': search, 'users': users,
                   'user_id': user_id, 'queryset_normal': queryset_normal.count()}

        return render(request, 'vm_shelve_list.html', context=context)


class VmUnShelveView(View):
    """恢复虚拟机类视图"""

    def get(self, request, *args, **kwargs):
        vm_uuid = kwargs.get('vm_uuid', '')
        vm = VmManager().get_vm_by_uuid(vm_uuid=vm_uuid, related_fields=())
        if not vm:
            error = VmError(code=400, msg='查询错误，无虚拟机')
            return error.render(request=request)

        if not vm.user_has_perms(user=request.user):
            error = VmError(code=400, msg='当前用户没有权限访问此虚拟机。')
            return error.render(request=request)

        center_id = 0
        if vm.last_ip:
            center_id = vm.last_ip.vlan.group.center_id

        try:
            c_manager = CenterManager()
            centers = c_manager.get_center_queryset()
            if len(centers) > 0:
                center_id = centers.first().id
            groups = c_manager.get_user_group_queryset_by_center(center_id, user=request.user)
        except ComputeError as e:
            error = ComputeError(msg='查询数据中心列表时错误', err=e)
            return error.render(request=request)

        context = {
            'center_id': center_id if center_id > 0 else None,
            'centers': centers,
            'groups': groups,
            'vm_uuid': vm_uuid,
            'last_ip': vm.last_ip,
        }
        return render(request, 'vm_unshelve.html', context=context)


class VmAttachIPView(View):
    NUM_PER_PAGE = 20  # Show num per page

    def get(self, request, *args, **kwargs):
        vm_uuid = kwargs.get('vm_uuid', '')
        vlan_id = str_to_int_or_default(request.GET.get('vlan-select', 0), 0)
        search = request.GET.get('search', '')
        vm = VmManager().get_vm_by_uuid(vm_uuid=vm_uuid, related_fields=('mac_ip',))

        if not vm.user_has_perms(user=request.user):
            error = VmError(code=400, msg='当前用户没有权限访问此虚拟机。')
            return error.render(request=request)

        if vlan_id != 0:

            qs = MacIPManager().get_free_ip_in_vlan(vlan_id=vlan_id, flag=True)
        else:
            qs = MacIPManager().get_free_ip_in_vlan(vlan_id=vm.mac_ip.vlan_id, flag=True)

        if search:
            qs = qs.filter(Q(ipv4__icontains=search) | Q(ipv6__icontains=search))

        vlan = VlanManager().get_vlan_queryset()

        context = self.get_ip_list_context(request, qs, context={'vm_uuid': vm_uuid, 'vlan_id': vm.mac_ip.vlan_id,
                                                                 'vm': vm, 'vlan': vlan})
        return render(request, 'vm_attach_ip_list.html', context=context)

    def get_ip_list_context(self, request, queryset, context: dict):
        # 分页显示
        paginator = NumsPaginator(request, queryset, self.NUM_PER_PAGE)
        page_num = request.GET.get(paginator.page_query_name, 1)  # 获取页码参数，没有参数默认为1
        mac_page = paginator.get_page(page_num)
        page_nav = paginator.get_page_nav(mac_page)

        context['page_nav'] = page_nav
        context['mac_ip'] = mac_page
        context['count'] = paginator.count
        return context


class VmDetachIPView(View):
    NUM_PER_PAGE = 20  # Show num per page

    def get(self, request, *args, **kwargs):
        vm_uuid = kwargs.get('vm_uuid', '')
        vm = VmManager().get_vm_by_uuid(vm_uuid=vm_uuid, related_fields=('mac_ip',))

        if not vm.user_has_perms(user=request.user):
            error = VmError(code=400, msg='当前用户没有权限访问此虚拟机。')
            return error.render(request=request)

        use_ip = AttachmentsIP.objects.select_related('sub_ip').filter(vm=vm).all()

        context = self.get_ip_list_context(request, use_ip, context={'vm_uuid': vm_uuid, 'vm': vm})
        return render(request, 'vm_detach_ip_list.html', context=context)

    def get_ip_list_context(self, request, queryset, context: dict):
        # 分页显示
        paginator = NumsPaginator(request, queryset, self.NUM_PER_PAGE)
        page_num = request.GET.get(paginator.page_query_name, 1)  # 获取页码参数，没有参数默认为1
        mac_page = paginator.get_page(page_num)
        page_nav = paginator.get_page_nav(mac_page)

        context['page_nav'] = page_nav
        context['mac_ip'] = mac_page
        context['count'] = paginator.count
        return context


class VmImageRelease(View):
    """镜像发布"""

    def get(self, request, *args, **kwargs):
        vm_uuid = kwargs.get('vm_uuid', '')
        vm = VmManager().get_vm_by_uuid(vm_uuid=vm_uuid, related_fields=('image',))
        context = {'vm_uuid': vm_uuid, 'vm': vm, 'image': vm.image}
        return render(request, 'vm_image_release.html', context=context)

    def post(self, request, *args, **kwargs):
        """
        1. 获取vm
        2. 克隆一个快照 name:test1
        3. flatten test1 这个快照
        4. 成功后信息写入数据库 操作系统镜像
        5. 成功返回
        """
        vm_uuid = kwargs.get('vm_uuid', '')
        try:
            vm = VmManager().get_vm_by_uuid(vm_uuid=vm_uuid, related_fields=('image',))
        except Exception as e:
            error = VmError(code=400, msg=str(e))
            return error.render(request=request)

        image_name = request.POST.get('image_name')
        if not image_name:
            # error = VmError(code=400, msg=f'镜像名称不能为空。')
            # return error.render(request=request)
            JsonResponse.status_code = 400
            return JsonResponse({'status': 400, 'msg': f'镜像名称不能为空。'}, json_dumps_params={'ensure_ascii': False})

        image_name = '-user_' + image_name  # 新的镜像名称

        vm_api = VmAPI()
        log_manager = VmLogManager()

        user = request.user.username
        try:

            vm_api.vm_user_release_image(vm=vm, new_image_name=image_name, user=user, log_manager=log_manager)
        except Exception as e:
            # error = VmError(code=400, msg=str(e))
            # return error.render(request=request)
            JsonResponse.status_code = 400
            return JsonResponse({'status': 400, 'msg': str(e)}, json_dumps_params={'ensure_ascii': False})

        # 将新数据写入数据库

        image_label = request.POST.get('image_label')  # 镜像标签
        image_os_type = request.POST.get('image_os_type')  # 系统类型
        image_os_release = request.POST.get('image_os_release')  # 系统发行版本
        image_os_version = request.POST.get('image_os_version')  # 系统发行编号
        image_os_architecture = request.POST.get('image_os_architecture')  # 系统架构
        image_os_boot_mode = request.POST.get('image_os_boot_mode')  # 系统启动方式
        image_size = request.POST.get('image_size')  # 镜像大小
        image_default_user = request.POST.get('image_default_user')  # 系统默认登录用户名
        image_default_password = request.POST.get('image_default_password')  # 系统默认登录密码
        image_desc = request.POST.get('image_desc')  # 描述
        image_enable = request.POST.get('image_enable')  # 启用
        if image_enable == 'true':
            image_enable = True
        else:
            image_enable = False

        user_id = request.user.id
        try:
            # 镜像创建虚拟机不占用服务器 cpu 和 mem , 使用系统，不使用大业内存
            Image.objects.create(
                name=image_name,
                sys_type=int(image_os_type),
                version=image_os_version,
                release=image_os_release,
                architecture=int(image_os_architecture),
                boot_mode=int(image_os_boot_mode),
                ceph_pool=vm.ceph_pool,
                tag=int(image_label),
                base_image=image_name,
                enable=image_enable,
                xml_tpl=vm.image.xml_tpl,
                user_id=user_id,
                desc=image_desc,
                default_user=image_default_user,
                default_password=image_default_password,
                size=int(image_size)
            )
        except Exception as e:
            # error = ImageError(code=400, msg=f'image: {image_name} exists, the entered data cannot be saved. '
            #                                  f'Please contact the administrator.')
            # return error.render(request=request)

            msg = f'发布镜像数据保存失败，详细情况 ==》 用户：{user} 镜像名称：{image_name} 失败原因：{str(e)}'
            log_manager.add_log(title=f'发布镜像失败：{image_name}', about=log_manager.about.ABOUT_NORMAL, text=msg)

            JsonResponse.status_code = 400
            return JsonResponse({'status': 400,
                                 'msg': f'image: {image_name} exists, the entered data cannot be saved.Please contact the administrator.'},
                                json_dumps_params={'ensure_ascii': False})

        return JsonResponse({'msg': f'image: {image_name} release success.', }, status=200)


class SyncVMView(View):
    """ 通过部分信息数据创建虚拟机信息记录 """

    def get(self, request, *args, **kwargs):
        hosts = HostManager().get_all_hosts()
        images = Image.objects.all()

        return render(request, 'vms_sync.html', context={
            'hosts': hosts,
            'images': images,
        })

    def post(self, request, *args, **kwargs):
        """
        """
        hostip = request.POST.get('hostip')
        vm_uuid = request.POST.get('vm-uuid')
        image_id = request.POST.get('image-id')
        vm_mac_ip = request.POST.get('vm-mac-ip')
        vm_mac = request.POST.get('vm-mac')
        mac_ip = None

        if not request.user.is_superuser:
            return self.response_error(request, '您无权限操作。')

        if not vm_mac and not vm_mac_ip:
            return self.response_error(request, '必须填写MAC或IP任意一项信息')

        elif vm_mac_ip and vm_mac:
            mac_ip = MacIPManager().get_macip_by_mac(mac=vm_mac)
            if not mac_ip:
                return self.response_error(request, '指定的MAC信息不存在')
            if mac_ip.ipv4 != vm_mac_ip:
                return self.response_error(request,
                                           f'指定的MAC和IP信息不匹配，MAC指定的IP信息为{mac_ip.ipv4}，不是{vm_mac_ip}')
        elif vm_mac:
            mac_ip = MacIPManager().get_macip_by_mac(mac=vm_mac)
            if not mac_ip:
                return self.response_error(request, '指定的MAC信息不存在')

        elif vm_mac_ip:
            mac_ip = MacIPManager().get_macip_by_ipv4(ipv4=vm_mac_ip)
            if not mac_ip:
                return self.response_error(request, '指定的IP信息不存在')

        host = HostManager().get_host_by_id(host_id=int(hostip))
        if host is None:
            return self.response_error(request, '宿主机不存在')

        if not vm_uuid:
            return self.response_error(request, '必须填写uuid')

        vm_domain = VmDomain(host_ip=host.ipv4, vm_uuid=vm_uuid)
        if vm_domain.exists() is False:
            return self.response_error(request, '虚拟机不存在')

        image = Image.objects.filter(id=int(image_id)).first()
        if image is None:
            return self.response_error(request, '镜像不存在')

        vm_obj = VmManager().get_vm_by_uuid(vm_uuid=vm_uuid)
        if vm_obj:
            return self.response_error(request, f'{vm_uuid}, 该虚拟机存在数据库中')

        try:
            cpu, mem = vm_domain.get_cpu_and_mem()
            # ip_dict = vm_domain.get_ipaddress_and_mac()  # 获取mac 信息
            vm_xml = vm_domain.get_vm_xml()
        except Exception as e:
            return self.response_error(request, f'获取虚拟机详细信息错误：{str(e)}')

        apply_ip = MacIPManager().apply_for_free_ip(ipv4=mac_ip.ipv4)  # 占用 IP信息
        if apply_ip is None:
            return self.response_error(request, '申请IP信息失败，请查看IP是否被占用')
        try:
            HostManager().claim_from_host(host_id=host.id, vcpu=cpu, mem=mem)  # 宿主机cpu 和内存资源修改

        except Exception as e:
            MacIPManager().free_used_ip(ipv4=mac_ip.ipv4)
            return self.response_error(request, f'释放IP, 更改宿主机CPU和内存信息失败：{str(e)}')

        host.vm_created_num_add_1()  # 尽量同步信息

        # 创建 vm 信息
        try:
            vm = Vm(
                uuid=vm_uuid,
                name=vm_uuid,
                vcpu=cpu,
                mem=mem,
                disk=vm_uuid,
                user=request.user,
                host=host,
                xml=vm_xml,
                mac_ip=mac_ip,
                image=image,
                image_name=image.name,
                image_parent=image.base_image,
                image_size=image.size,
                version=image.version,
                release=image.release,
                architecture=image.architecture,
                boot_mode=image.boot_mode,
                ceph_pool=image.ceph_pool,
                image_xml_tpl=image.xml_tpl.xml,
                image_desc='手动同步',
                sys_disk_size=image.size,
            )
            vm.save()
        except Exception as e:
            MacIPManager().free_used_ip(ipv4=mac_ip.ipv4)
            HostManager().free_to_host(host_id=host.id, vcpu=cpu, mem=mem)
            host.vm_created_num_sub_1()
            return self.response_error(request, f'创建虚拟机信息错误：{str(e)}')

        user_operation_record.add_log(request=request,
                                      operation_content=f'手动同步虚拟机信息到数据库：vm_uuid={vm_uuid}', remark='')

        return redirect('vms:vms-list')

    @staticmethod
    def response_error(request, err_msg):
        hosts = HostManager().get_all_hosts()
        images = Image.objects.all()
        return render(request, 'vms_sync.html', context={
            'hosts': hosts,
            'images': images,
            'err_msg': err_msg,
        })


class VmRecover(View):
    """虚拟机归档恢复列表"""
    NUM_PER_PAGE = 20

    def get(self, request, *args, **kwargs):

        search = request.GET.get('search', '')

        # 区分管理员搜索和普通用户搜索
        if search and request.user.is_superuser:
            queryset = VmArchive.objects.filter(
                Q(uuid__contains=search) | Q(ipv4__contains=search) | Q(user__contains=search))
        elif search:
            queryset = VmArchive.objects.filter(user=request.user.username).filter(
                Q(uuid__contains=search) | Q(ipv4__contains=search))
        elif request.user.is_superuser:
            queryset = VmArchive.objects.all()
        else:
            queryset = VmArchive.objects.filter(user=request.user.username).all()

        queryset = queryset.order_by('-id')
        context = {}
        context = self.get_vms_list_context(request, queryset, context)

        return render(request, 'vm_recover_list.html', context=context)

    def post(self, request, *args, **kwargs):

        uuid = request.GET.get('uuid', '')
        vm_archid = request.GET.get('vm_archid', '')
        xml = request.GET.get('xml', '')

        xml_construct = False
        if xml and xml == 'true':
            xml_construct = True

        if not request.user.is_superuser:
            JsonResponse.status_code = 400
            return JsonResponse({'status': 400,
                                 'msg': f'error: 您无权限.'},
                                json_dumps_params={'ensure_ascii': False})

        vm_arch = VmArchive.objects.filter(id=int(vm_archid), uuid=uuid, disk__isnull=False).first()
        # vm_arch = VmArchive.objects.filter(uuid=uuid).first()
        if not vm_arch:
            JsonResponse.status_code = 400
            return JsonResponse({'status': 400,
                                 'msg': f'error: 指定的归档虚拟机不存在.'},
                                json_dumps_params={'ensure_ascii': False})

        vm_obj = Vm.objects.filter(uuid=uuid).first()
        if vm_obj:
            JsonResponse.status_code = 400
            return JsonResponse({'status': 400,
                                 'msg': f'error: 虚拟机id已存在，无法恢复.'},
                                json_dumps_params={'ensure_ascii': False})

        mac_id = None
        old_mac = MacIPManager().get_macip_by_mac(mac=vm_arch.mac)
        if not old_mac.used:
            mac_id = old_mac.id

        image_obj = ImageManager().get_image_by_id(image_id=vm_arch.image_id)

        try:
            self.vm_archive_recover(vm_arch=vm_arch, image=image_obj, mac_id=mac_id, request=request, xml_construct=xml_construct)
        except Exception as e:
            user_operation_record.add_log(request=request,
                                          operation_content=f'虚拟机：{vm_arch.name} 归档恢复失败， 恢复用户：{vm_arch.user}，'
                                                            f'错误信息：{str(e)}',
                                          remark='', owner=request.user)
            JsonResponse.status_code = 400
            return JsonResponse({'status': 400,
                                 'msg': f'error: {str(e)}.'},
                                json_dumps_params={'ensure_ascii': False})

        user_operation_record.add_log(request=request,
                              operation_content=f'虚拟机：{vm_arch.name} 归档恢复成功， 恢复用户：{vm_arch.user}',
                              remark='', owner=request.user)

        return JsonResponse({'msg': f'succeed: The archive VM has been recovered.'})


    def vm_archive_recover(self, vm_arch, image, mac_id, request, xml_construct):
        """
        归档虚拟机恢复
        :param request:
        :param mac_id:
        :param image:
        :param vm_arch:
        :return:
        """

        user = User.objects.filter(username=vm_arch.user).first()
        if not user:
            raise Exception(f'用户不存在')

        if vm_arch.host_id == 0:
            raise Exception(f'虚拟机归档数据不正确: 宿主机id信息不对')

        if vm_arch.group_id == 0:
            raise Exception(f'虚拟机归档数据不正确: 宿主机组id信息不对')

        old_image_name = vm_arch.disk

        if not old_image_name:
            raise Exception(f'未找到{vm_arch.uuid}归档表中,系统盘名称')

        create_vm = Vm(
            uuid=vm_arch.uuid,
            name=vm_arch.name,
            vcpu=vm_arch.vcpu,
            mem=vm_arch.mem,
            disk=vm_arch.uuid,
            image_id=vm_arch.image_id,
            user=user,
            host_id=vm_arch.host_id,
            xml=vm_arch.xml,
            mac_ip=None,
            image_name=image.name,
            image_parent=vm_arch.image_parent,
            image_snap=image.snap,
            image_size=image.size,
            ceph_pool_id=vm_arch.ceph_id,
            vnet_bandwidth=vm_arch.vnet_bandwidth,
        )

        try:
            create_vm.save()
        except Exception as e:
            raise e

        try:

            VmBuilder().archive_vm_create(vm=create_vm, group_id=vm_arch.group_id, host_id=vm_arch.host_id,
                                          mac_ip_id=mac_id, user=user, old_image_name=old_image_name,
                                          xml_construct=xml_construct)
        except Exception as e:
            raise e

        try:
            vm_arch.delete()
        except Exception as e:
            logger.error(msg=f'虚拟机归档恢复成功,无法删除归档表中的数据: uuid={vm_arch.uuid}, 错误信息: {str(e)}')
            pass


    def get_vms_list_context(self, request, vms_queryset, context: dict):
        # 分页显示
        paginator = NumsPaginator(request, vms_queryset, self.NUM_PER_PAGE)
        page_num = request.GET.get(paginator.page_query_name, 1)  # 获取页码参数，没有参数默认为1
        vms_page = paginator.get_page(page_num)
        page_nav = paginator.get_page_nav(vms_page)

        context['page_nav'] = page_nav
        context['vm_recover_list'] = vms_page
        context['count'] = paginator.count
        return context


class VmBandWidth(View):
    def get(self, request, *args, **kwargs):
        vm_uuid = kwargs.get('vm_uuid', '')

        vm_manager = VmManager()
        vm = vm_manager.get_vm_by_uuid(vm_uuid=vm_uuid, related_fields=('host__group', 'image__ceph_pool',
                                                                        'mac_ip__vlan'))
        if not vm:
            JsonResponse.status_code = 400
            return JsonResponse({'status': 400,
                                 'msg': f'error: 虚拟机不存在.'},
                                json_dumps_params={'ensure_ascii': False})

        if request.user.is_superuser:
            return render(request, 'vm_edit_bandwidth.html', context={'vm': vm})

        if vm.user != request.user:
            JsonResponse.status_code = 400
            return JsonResponse({'status': 400,
                                 'msg': f'error: 无权限.'},
                                json_dumps_params={'ensure_ascii': False})

        return render(request, 'vm_edit_bandwidth.html', context={'vm':vm})